#include <Arduino.h>
#include <WiFi.h>
#include "hub75e.h"
#include "ClockManager.h"
#include "OTAManager.h"
#include "espWifiCfg.h"
#include "ntpTime.h"
#include "display_config.h"
#include "fonts.h"

// 引脚定义 (使用宏定义，便于维护和理解)
const uint8_t pins[14] = {PIN_CLK, PIN_LE, PIN_OE, PIN_A, PIN_B, PIN_C, PIN_D, PIN_E, PIN_R1, PIN_G1, PIN_B1, PIN_R2, PIN_G2, PIN_B2};

Panel panel(DISPLAY_WIDTH, DISPLAY_HEIGHT, NUM_SCAN, pins);
EspWifiCfg wifiCfg;
NTPTime ntpTime;
ClockManager clockManager(panel, ntpTime);
OTAManager otaManager(panel);

// 状态变量
bool wifiConnected = false;
unsigned long lastTimeUpdate = 0;
unsigned long lastClockUpdate = 0;
unsigned long lastNTPAttempt = 0;
const unsigned long NTP_UPDATE_INTERVAL = 3600000;
const unsigned long CLOCK_UPDATE_INTERVAL = 1000;

// Helper function to draw text using 5x8 font with left alignment
void drawText5x8(uint16_t x, uint16_t y, const char* text, uint8_t r, uint8_t g, uint8_t b) {
    uint16_t currentX = x;
    for (size_t i = 0; text[i] != '\0'; ++i) {
        int idx = fontIndex(text[i]);
        for (uint8_t row = 0; row < 8; ++row) {
            uint8_t bits = font5x8[idx][row];
            for (uint8_t col = 0; col < 5; ++col) {
                if (bits & (1 << (4 - col))) {
                    panel.drawPixel(currentX + col, y + row, r, g, b);
                }
            }
        }
        currentX += 6; // 5 pixels wide + 1 pixel spacing
    }
}

// OTA functionality is now handled by OTAManager class

// 绘制状态信息（使用 panel.width / panel.height，避免 relying on macros at runtime）
void drawStatusInfo() {
    // Skip status indicators if OTA is in progress (top area reserved for OTA)
    uint16_t statusStartY = otaManager.isInProgress() ? 12 : 0;
    
    // Optimized status drawing with reduced calculations and improved readability
    constexpr uint16_t indicatorSize = 4; // 4x4 pixel status squares
    constexpr uint16_t gap = 1; // gap between indicators
    uint16_t wifiY = statusStartY;
    uint16_t ntpY = statusStartY + indicatorSize + gap;
    
    // Wi-Fi status indicator (top-left or below OTA area)
    uint8_t r, g, b;
    if (wifiCfg.reconfigNeeded()) {
        // Blink red for reconfiguration needed
        bool on = ((millis() / 400) & 1) == 0; // 2.5Hz blink, bit operation faster than modulo
        r = on ? 200 : 0;  // Slightly dimmed for better visibility
        g = b = 0;
    } else if (wifiConnected) {
        r = 0; g = 200; b = 0; // Green when connected
    } else {
        r = 200; g = b = 0; // Red when disconnected
    }
    panel.fillRect(0, wifiY, indicatorSize - 1, wifiY + indicatorSize - 1, r, g, b);
    
    // NTP status indicator (below Wi-Fi)
    if (ntpTime.isTimeValid()) {
        panel.fillRect(0, ntpY, indicatorSize - 1, ntpY + indicatorSize - 1, 0, 0, 200); // Blue
    } else {
        panel.fillRect(0, ntpY, indicatorSize - 1, ntpY + indicatorSize - 1, 200, 200, 0); // Yellow
    }
}

void setup() {
    Serial.begin(115200);
    
    // Initialize display first
    panel.begin();
    panel.clearScreen();
    panel.setBackgound(0, 0, 0); // Black background
    
    // Boot splash screen with startup information
    Serial.println("=== " FIRMWARE_NAME " ===");
    
    // Display project name at top with version
    const char* projectName = FIRMWARE_NAME;
    drawText5x8(2, 1, projectName, 0, 255, 0); // Green text, top-aligned
    Serial.println("Display initialized");
    delay(2000); // Show project name longer
    
    // Show initialization steps with clear spacing
    drawText5x8(2, 11, "Initializing...", 255, 255, 0); // Yellow for initialization
    Serial.println("Starting system initialization...");
    delay(1500);
    
    // Clear previous message and show WiFi setup
    panel.fillRect(0, 11, panel.width - 1, 19, 0, 0, 0); // Clear line
    drawText5x8(2, 11, "WiFi Setup...", 0, 255, 255); // Cyan for WiFi
    Serial.println("Starting WiFi configuration...");
    wifiCfg.begin();
    delay(2000);

    // Show WiFi connection status
    panel.fillRect(0, 11, panel.width - 1, 19, 0, 0, 0); // Clear previous message
    String ssid = WiFi.SSID();
    String ip = WiFi.localIP().toString();
    
    if (WiFi.status() == WL_CONNECTED) {
        drawText5x8(2, 11, "WiFi Connected", 0, 255, 0); // Green for success
        Serial.println("WiFi connected successfully");
    } else {
        drawText5x8(2, 11, "WiFi AP Mode", 255, 255, 0); // Yellow for AP mode
        ssid = "AP:" + String(WiFi.softAPSSID());
        ip = WiFi.softAPIP().toString();
        Serial.println("WiFi in AP mode");
    }
    
    // Show SSID and IP on separate lines
    char ssidBuf[32];
    char ipBuf[32];
    snprintf(ssidBuf, sizeof(ssidBuf), "SSID:%s", ssid.c_str());
    snprintf(ipBuf, sizeof(ipBuf), "IP:%s", ip.c_str());
    drawText5x8(2, 20, ssidBuf, 255, 255, 255); // White text
    drawText5x8(2, 29, ipBuf, 255, 255, 255);   // White text
    delay(3000);

    // Initialize NTP
    panel.fillRect(0, 38, panel.width - 1, 46, 0, 0, 0); // Clear area for NTP status
    drawText5x8(2, 38, "NTP Setup...", 0, 255, 255); // Cyan for NTP
    Serial.println("Starting NTP service...");
    ntpTime.begin();
    delay(1500);
    
    // Initialize OTA when WiFi is ready
    if (WiFi.status() == WL_CONNECTED) {
        panel.fillRect(0, 38, panel.width - 1, 46, 0, 0, 0); // Clear NTP area
        drawText5x8(2, 38, "OTA Setup...", 255, 0, 255); // Magenta for OTA
        
        if (otaManager.begin("CalClock-ESP32", "calclockota123")) {
            // Show OTA ready status
            panel.fillRect(0, 38, panel.width - 1, 46, 0, 0, 0); // Clear previous message
            drawText5x8(2, 38, "OTA Ready", 0, 255, 0); // Green for ready
            delay(2000);
        } else {
            panel.fillRect(0, 38, panel.width - 1, 46, 0, 0, 0); // Clear previous message
            drawText5x8(2, 38, "OTA N/A", 255, 255, 0); // Yellow for not available
            delay(1500);
        }
    } else {
        // Show OTA not available
        panel.fillRect(0, 38, panel.width - 1, 46, 0, 0, 0); // Clear previous message
        drawText5x8(2, 38, "OTA N/A", 255, 255, 0); // Yellow for not available
        Serial.println("OTA not available (no WiFi)");
        delay(1500);
    }

    // System ready - show final status
    panel.fillRect(0, 38, panel.width - 1, 56, 0, 0, 0); // Clear status area
    drawText5x8(2, 38, "System Ready!", 0, 255, 0); // Green for ready
    drawText5x8(2, 47, "Starting...", 255, 255, 0);   // Yellow for starting
    Serial.println("System initialization complete");
    delay(3000); // Show ready message longer
    
    // Keep system/local timezone as configured (default). We'll request the right panel time
    // explicitly for San Francisco when drawing.

    panel.clearScreen();
    // Draw initial clocks immediately (even before Wi‑Fi/NTP) so boot isn't blank
    clockManager.updateIfNeeded(lastClockUpdate, 0, -8, false); // OTA not possible during init
    drawStatusInfo();
    Serial.println("ESP32 LED clock started");
}

void loop() {
    wifiCfg.loop();
    
    // Handle OTA updates
    otaManager.handle();

    // 检查WiFi状态（封装到 EspWifiCfg）
    bool wifiStatusChanged = wifiCfg.updateWifiStatus(wifiConnected, lastNTPAttempt);

    // NTP时间同步（封装到 NTPTime）
    if (wifiConnected) {
        // If we just connected, trigger a near-immediate sync once
        if (wifiStatusChanged) {
            ntpTime.attemptSyncIfNeeded(lastNTPAttempt, 1000);
        }
        ntpTime.attemptSyncIfNeeded(lastNTPAttempt, NTP_UPDATE_INTERVAL);
    }

    // 更新时钟显示（封装到 ClockManager）
    // Pass -8 to draw the right panel in San Francisco time
    // Pass OTA status to ClockManager for additional safety
    if (clockManager.updateIfNeeded(lastClockUpdate, CLOCK_UPDATE_INTERVAL, -8, otaManager.isInProgress())) {
        // Clock redrawn once per second (only when OTA not in progress)
    }

    // OTA display is now handled by OTAManager.handle() above

    // Redraw status indicators at optimal frequency for smooth blinking (skip during OTA)
    static unsigned long lastStatusDraw = 0;
    constexpr unsigned long statusUpdateInterval = 80; // 12.5 FPS - good balance between smoothness and CPU usage
    if (!otaManager.isInProgress() && millis() - lastStatusDraw >= statusUpdateInterval) {
        drawStatusInfo();
        lastStatusDraw = millis();
    }

    // Debug output showing left/right times (every 5 seconds)
    static unsigned long lastDebug = 0;
    if (millis() - lastDebug >= 5000) {
        lastDebug = millis();
        
        if (ntpTime.isTimeValid()) {
            // Get left panel time (local time)
            uint8_t leftHour = ntpTime.getHour();
            uint8_t leftMin = ntpTime.getMinute();
            uint8_t leftSec = ntpTime.getSecond();
            
            // Get right panel time (Pacific time with DST)
            uint8_t rightHour, rightMin, rightSec, wday;
            int year, month, day;
            bool hasRightTime = ntpTime.getPacificTime(rightHour, rightMin, rightSec, wday, year, month, day);
            
            if (hasRightTime) {
                Serial.printf("[%lu] Left(Local): %02d:%02d:%02d | Right(Pacific): %02d:%02d:%02d %s | Heap: %u\n",
                             millis(), leftHour, leftMin, leftSec, 
                             rightHour, rightMin, rightSec, ntpTime.getPacificTimeZoneAbbrev(),
                             ESP.getFreeHeap());
            } else {
                Serial.printf("[%lu] Left(Local): %02d:%02d:%02d | Right: N/A | Heap: %u\n",
                             millis(), leftHour, leftMin, leftSec, ESP.getFreeHeap());
            }
        } else {
            // Show system uptime when NTP not available
            unsigned long totalSeconds = millis() / 1000;
            uint8_t sysHour = (totalSeconds / 3600) % 24;
            uint8_t sysMin = (totalSeconds / 60) % 60;
            uint8_t sysSec = totalSeconds % 60;
            Serial.printf("[%lu] System uptime: %02d:%02d:%02d | NTP: Not synced | Heap: %u\n",
                         millis(), sysHour, sysMin, sysSec, ESP.getFreeHeap());
        }
    }

    delay(5); // Reduced from 10ms - better responsiveness
}